package newsletterService

import (
	"encoding/json"
	"errors"
	"github.com/Sir-yuChen/funAnalyse/global"
	"github.com/Sir-yuChen/funAnalyse/global/constant"
	"github.com/Sir-yuChen/funAnalyse/model"
	"github.com/Sir-yuChen/funAnalyse/model/common/ApiReturn"
	"github.com/Sir-yuChen/funAnalyse/model/request"
	"github.com/Sir-yuChen/funAnalyse/service/commonService"
	"github.com/Sir-yuChen/funAnalyse/service/timer"
	"github.com/Sir-yuChen/funAnalyse/utils"
	timer2 "github.com/Sir-yuChen/funAnalyse/utils/timer"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

type StockBulletinService struct{}

func NewStockBulletinService() *StockBulletinService {
	return &StockBulletinService{}
}

//GetStockBulletin 多条件查询
func (u *StockBulletinService) GetStockBulletin(req *model.StockBulletinModel) (a ApiReturn.ApiReturnCode) {
	var mo []model.StockBulletinModel
	sql := ""
	obj := model.NewStockBulletinModel()
	tableName := obj.TableName()
	sqlCondition := utils.ReflectHandleStuct(req)
	if sqlCondition != "" {
		sql = `select * from ` + tableName + ` where ` + sqlCondition
	} else {
		sql = `select * from ` + tableName + ` order by id desc  limit 100 `
	}
	err := global.GVA_DB.Raw(sql).Scan(&mo).Error
	if err != nil {
		global.GVA_LOG.Error("GetStockBulletin异常", zap.Any("request", req), zap.Any("sql", sql), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	s := ApiReturn.OK
	s.Data = mo
	return s
}

//UpdateStockBulletin 更新
func (u *StockBulletinService) UpdateStockBulletin(req *model.StockBulletinModel) (a ApiReturn.ApiReturnCode) {
	if req.Id == 0 {
		return ApiReturn.ErrParam
	}
	err := global.GVA_DB.Model(model.StockBulletinModel{}).Updates(req).Error
	if err != nil {
		global.GVA_LOG.Error("UpdateStockBulletin异常", zap.Any("request", req), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	return ApiReturn.OK
}

//DelStockBulletin 根据ID删除
func (u *StockBulletinService) DelStockBulletin(req *model.StockBulletinModel) (a ApiReturn.ApiReturnCode) {
	if req.Id == 0 {
		return ApiReturn.ErrParam
	}
	err := global.GVA_DB.Model(model.StockBulletinModel{}).Where("id = ?", req.Id).Delete(model.StockBulletinModel{}).Error
	if err != nil {
		global.GVA_LOG.Error("DelStockBulletin异常", zap.Any("request", req), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	return ApiReturn.OK
}

//InsertStockBulletin 新增公告
func (u *StockBulletinService) InsertStockBulletin(req *request.InsertBulletinRequest, c *gin.Context) (a ApiReturn.ApiReturnCode) {
	var groupInfo model.StockUserGroupModel
	if err := global.GVA_DB.Model(&groupInfo).Where(" id = ? ", req.BulletinGroupId).First(&groupInfo).Error; err != nil {
		if err.Error() == constant.ERROR_NO_DATA {
			global.GVA_LOG.Error("未查询到公告面向的人群", zap.Any("request", req), zap.Error(err))
			return ApiReturn.NoGroupInfo
		}
		global.GVA_LOG.Error("查询公告面向人群异常", zap.Any("request", req), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	if groupInfo.Status != constant.STATUS_Y {
		return ApiReturn.GroupDisabled
	}
	a = CheckParams(req)
	if a.Code != ApiReturn.OK.Code {
		return a
	}
	userInfo, err := commonService.GetUserInfoByToken(c)
	if err != nil {
		global.GVA_LOG.Error("获取当前操作账号信息异常", zap.Error(err))
		return ApiReturn.ErrSystem
	}
	var total int64
	if err := global.GVA_DB.Model(&model.StockBulletinModel{}).Where(" bulletin_name  = ? ", req.BulletinName).Count(&total).Error; err != nil {
		global.GVA_LOG.Error("校验公告名称不能重复异常", zap.Any("request", req), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	if total > 0 {
		return ApiReturn.ExistingBulletinName
	}
	start, _ := time.Parse("2006-01-02 15:04:05", req.SendStartTime)
	end, _ := time.Parse("2006-01-02 15:04:05", req.SendEndTime)
	//校验通过组装参数
	st := model.LocalTime(start)
	et := model.LocalTime(end)
	now := model.LocalTime(time.Now())
	var bull = model.StockBulletinModel{
		BulletinMainTitle:      req.BulletinMainTitle,
		BulletinSubtitleTitle:  req.BulletinSubtitleTitle,
		BulletinName:           req.BulletinName,
		BulletinGroupId:        req.BulletinGroupId,
		Status:                 req.Status,
		BulletinType:           req.BulletinType,
		BulletinContent:        req.BulletinContent,
		SendType:               req.SendType,
		IntervalTime:           req.IntervalTime,
		NoSendStartDisturbTime: req.NoSendStartDisturbTime,
		NoSendEndDisturbTime:   req.NoSendEndDisturbTime,
		SendStartTime:          &st,
		SendEndTime:            &et,
		CreateBy:               userInfo.CustomerName,
		CreateTime:             &now,
		LastUpdTime:            &now,
	}
	err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Model(model.StockBulletinModel{}).Create(&bull).Error
		if err != nil {
			global.GVA_LOG.Error("InsertStockBulletin异常", zap.Any("request", req), zap.Error(err))
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		uid, _ := uuid.NewUUID()
		timerParameter := map[string]interface{}{
			"bulletinName": req.BulletinName,
		}
		marshal, err := json.Marshal(timerParameter)
		if err != nil {
			global.GVA_LOG.Error("InsertStockBulletin异常 数据转换异常", zap.Any("request", req), zap.Error(err))
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		timerModel := model.StockTImer{
			Execution:       "BulletinHandleTimer",
			Status:          constant.STATUS_Y,
			CreateBy:        userInfo.CustomerName,
			TimerName:       req.BulletinName,
			Remark:          "处理推送公告定时任务",
			ExecutionStatus: constant.TIMER_ExecutionStatus_not_run,
			UniqueCode:      strings.Replace(uid.String(), "-", "", -1),
			Parameter:       string(marshal),
			CreateTime:      &now,
			LastUpdTime:     &now,
		}
		split := strings.Split(req.IntervalTime, ",")
		unitSplit := strings.Split(req.IntervalUnit, ",")
		intervalMap := map[string]int64{}
		for i, v := range split {
			atoi, _ := strconv.Atoi(v)
			intervalMap[unitSplit[i]] = int64(atoi)
		}
		//创建定时任务，实现自动发送   根据一定规则生成CRON表达式
		switch int(bull.SendType) {
		case constant.BULLETIN_SEND_TYPE_NOW: //立刻发送
			timerModel.TimerType = constant.TIMER_TASK_TYPE_disposable
			nowTime := time.Now()
			nowTime = nowTime.Add(2 * time.Minute) //加载代码需要时间延迟2分钟
			localTime := model.LocalTime(nowTime)
			timerModel.TimerStartTime = &localTime
			bull.SendStartTime = &localTime
			cronStr := timer2.CreateCronStr(intervalMap, bull.SendStartTime.ToTime(), false)
			timerModel.TimerSpec = cronStr
			break
		case constant.BULLETIN_SEND_TYPE_TIMING: //定时发送
			timerModel.TimerType = constant.TIMER_TASK_TYPE_disposable
			timerModel.TimerStartTime = bull.SendStartTime
			cronStr := timer2.CreateCronStr(intervalMap, bull.SendStartTime.ToTime(), false)
			timerModel.TimerSpec = cronStr
			break
		case constant.BULLETIN_SEND_TYPE_INTERVAL: //重复发送
			timerModel.TimerType = constant.TIMER_TASK_TYPE_repeat
			timerModel.TimerStartTime = bull.SendStartTime
			timerModel.TimerEndTime = bull.SendEndTime
			cronStr := timer2.CreateCronStr(intervalMap, bull.SendStartTime.ToTime(), true)
			timerModel.TimerSpec = cronStr
			break
		default:
			global.GVA_LOG.Error("未知的公告推送类型", zap.Any("request", req))
			return errors.New(ApiReturn.BulletinFailed.Msg)
		}
		if err = tx.Model(&timerModel).Create(&timerModel).Error; err != nil {
			global.GVA_LOG.Error("公告新增定时任务异常", zap.Any("StockBulletinModel", bull), zap.Any("StockTImer", timerModel), zap.Error(err))
			return errors.New("公告新增,生成定时任务异常")
		}
		err = timer.AddTaskJob(timerModel, nil, nil)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		failed := ApiReturn.BulletinFailed
		failed.Msg = err.Error()
		return failed
	}
	return ApiReturn.OK
}

func CheckParams(req *request.InsertBulletinRequest) ApiReturn.ApiReturnCode {
	failed := ApiReturn.BulletinFailed
	//1.当发生方式为循环重复发送 则校验勿扰时间
	if int(req.SendType) == constant.BULLETIN_SEND_TYPE_NOW {
		req.SendStartTime = time.Now().Format("2006-01-02 15:04:05")
	}
	if (int(req.SendType) == constant.BULLETIN_SEND_TYPE_TIMING || int(req.SendType) == constant.BULLETIN_SEND_TYPE_INTERVAL) && req.SendStartTime == "" {
		global.GVA_LOG.Error("新增公告,定时发送/重复循环发送方式,发送时间不能为空", zap.Any("request", req))
		failed.Msg = "定时/重复循环发送公告,请设置发送时间"
		return failed
	}
	if req.SendStartTime != "" {
		t1, err := time.Parse("2006-01-02 15:04:05", req.SendStartTime)
		t2, err := time.Parse("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"))
		if err == nil && t1.Before(t2) {
			global.GVA_LOG.Error("新增公告,发送时间必须大于当前时间", zap.Any("request", req))
			failed.Msg = "发送公告,发送时间必须大于当前时间"
			return failed
		}
	}
	//时间段内循环重复发送，要求发送时间，结束时间，毕传，如果勿扰时间有传检验，勿扰时间是否符合标准 时间间隔也不能为空
	if int(req.SendType) == constant.BULLETIN_SEND_TYPE_INTERVAL {
		if req.SendStartTime == "" || req.SendEndTime == "" {
			global.GVA_LOG.Error("新增公告,重复循环发送,发送时间,结束时间不能为空", zap.Any("request", req))
			failed.Msg = "定时发送公告,请设置发送时间"
			return failed
		}
		//开始时间小于结束时间
		t1, err := time.Parse("2006-01-02 15:04:05", req.SendStartTime)
		t2, err := time.Parse("2006-01-02 15:04:05", req.SendEndTime)
		if err == nil && t1.After(t2) {
			global.GVA_LOG.Error("新增公告,重复循环发送,开始时间应小于结束时间", zap.Any("request", req))
			failed.Msg = "重复循环发送公告,开始时间应小于结束时间"
			return failed
		}
		if req.NoSendStartDisturbTime != "" || req.NoSendEndDisturbTime != "" { //勿扰时间
			startSplit := strings.Split(req.NoSendStartDisturbTime, ":")
			endSplit := strings.Split(req.NoSendEndDisturbTime, ":")
			if len(startSplit) != len(endSplit) && (len(startSplit) != 2 || len(endSplit) == 2) {
				global.GVA_LOG.Error("新增公告,循环重复发送公共,勿扰时间格式不正确", zap.Any("request", req))
				failed.Msg = "循环重复发送公共,请按照固定格式设置勿扰时间,可跨天[以天为单位,05:22]"
				return failed
			}
			b := checkDisturbTime(startSplit[0], startSplit[1])
			bo := checkDisturbTime(endSplit[0], endSplit[1])
			if !b || !bo {
				failed.Msg = "循环重复发送公共,请按照固定格式设置勿扰时间,可跨天[以天为单位,05:22]"
				return failed
			}
		}
		//对时间间隔的校验
		if req.IntervalTime == "" || req.IntervalUnit == "" {
			failed.Msg = "循环重复发送公共,请设置重复发送的间隔时间以及时间间隔单位"
			return failed
		} else {
			split := strings.Split(req.IntervalTime, ",")
			unitSplit := strings.Split(req.IntervalUnit, ",")
			if len(split) != len(unitSplit) {
				failed.Msg = "循环重复发送公共,时间间隔与时间间隔单位不匹配"
				return failed
			}
			for i, v := range split {
				atoi, _ := strconv.Atoi(v)
				differ, e := getHourDiffer(req.SendStartTime, req.SendEndTime, unitSplit[i], atoi)
				if e != nil {
					failed.Msg = e.Error()
					return failed
				}
				if int64(atoi) > differ {
					failed.Msg = "循环重复发送公共,重复发送间隔时间不能大于等开始时间和结束时间区间"
					return failed
				}
			}
		}
	}
	if req.Status == "" {
		req.Status = constant.STATUS_Y
	}
	parse, _ := time.Parse("2006-01-02 15:04:05", req.SendStartTime)
	req.SendStartTime = parse.Add(2 * time.Minute).Format("2006-01-02 15:04:05")
	return ApiReturn.OK
}

func checkDisturbTime(h string, m string) bool {
	hi, _ := strconv.Atoi(h)
	mi, _ := strconv.Atoi(m)
	if hi >= 0 && hi <= 24 && mi >= 0 && mi <= 60 {
		return true
	}
	return false
}

//获取相差时间
func getHourDiffer(start_time, end_time, unit string, intervalTime int) (int64, error) {
	var s int64
	t1, err := time.ParseInLocation("2006-01-02 15:04:05", start_time, time.Local)
	t2, err := time.ParseInLocation("2006-01-02 15:04:05", end_time, time.Local)
	if err == nil && t1.Before(t2) {
		diff := t2.Unix() - t1.Unix()
		//根据单位计算
		switch unit {
		case constant.Interval_Unit_Minute:
			if intervalTime <= 0 || intervalTime > 59 {
				return 0, errors.New("选择的时间间隔单位为【分钟】阈值为[0~59]")
			}
			s = diff / 60
			break
		case constant.Interval_Unit_Year:
			if intervalTime < 0 || intervalTime > 5 {
				return 0, errors.New("选择的时间间隔单位为【年】阈值为[0~5]")
			}
			s = diff / 60 / 60 / 24 / 365
			break
		case constant.Interval_Unit_Month:
			if intervalTime < 1 || intervalTime > 12 {
				return 0, errors.New("选择的时间间隔单位为【月】阈值为[1~12]")
			}
			s = diff / 60 / 60 / 24 / 30
			break
		case constant.Interval_Unit_Hour:
			if intervalTime < 0 || intervalTime > 23 {
				return 0, errors.New("选择的时间间隔单位为【小时】阈值为[0~23]")
			}
			s = diff / 60 / 60
			break
		case constant.Interval_Unit_Week:
			if intervalTime < 1 || intervalTime > 7 {
				return 0, errors.New("选择的时间间隔单位为【周】阈值为[1~7]")
			}
			s = diff / 60 / 60 / 24 / 7
			break
		case constant.Interval_Unit_Day:
			if intervalTime < 1 || intervalTime > 31 {
				return 0, errors.New("选择的时间间隔单位为【天】阈值为[1~31]")
			}
			s = diff / 60 / 60 / 24
			break
		default:
			return 0, errors.New("未知的时间间隔单位")
		}
	}
	return s, nil
}
